﻿package peace.gui.dnd
{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.geom.Rectangle;
/**
* 
*/
[Event(name="startDrag", type="peace.gui.dnd.DragEvent")] 
/**
* 
*/
[Event(name="stopDrag", type="peace.gui.dnd.DragEvent")] 
/**
* 
*/
[Event(name="move", type="peace.gui.dnd.DragEvent")] 
/**
 * Универсальный таскаемый Спрайт.
 */
public class Draggable extends Sprite
{
	/**
	 * таскается ли в данный момент
	 */
	protected var dragged:Boolean = false;
	/**
	 * граница таскания 
	 */	
	protected var bounds:Rectangle;
	/**
	 * локальный якорь начала таскания
	 */
	private var dragAnchor:Point;
	/**
	 * начальная позиция
	 */
	private var firstPosition:Point = new Point();
	/**
	 * задавал ли юзер границы таскания 
	 */	
	private var boundsInitialized:Boolean = false;
	/**
	 * 
	 */	
	private var _moveable:Boolean = true;
	/**
	 * 
	 */	
	private var _enabled:Boolean = true;
	/**
	 * 
	 * @param dragBoundsRect Rectangle
	 * 
	 */
	public function Draggable (dragBoundsRect:Rectangle = null)
	{
		addEventListener(Event.ADDED_TO_STAGE, onAddedToStage, false,0,true);
		addEventListener(Event.REMOVED_FROM_STAGE, onRemovedFromStage, false,0,true);
		
		bounds = dragBoundsRect;
	}
	/**
	 * Форсируем начало таскания
	 * 
	 * @param dragPoint
	 * 
	 */	
	public function beginDrag (dragPoint:Point):void
	{
		/**
		* если границ не определили в конструкторе, то граничимся сценой
		*/			
		if (bounds==null)
		{
			var p:Point = parent.globalToLocal(new Point(0, 0));
			bounds = new Rectangle(p.x, p.y, stage.stageWidth, stage.stageHeight);
		}
		
		if (_moveable)
		{
			stage.addEventListener(MouseEvent.MOUSE_UP, onRelease, false, 0, true);
			stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove, false, 0, true);
			
			dragged = true;
			dragAnchor = dragPoint;
			bringToFont();
			
			place(parent.mouseX - dragAnchor.x, parent.mouseY - dragAnchor.y);
		}
	}
	/**
	 * Форсируем окончание таскания 
	 * 
	 */
	public function endDrag ():void
	{
		if(stage)
		{
			stage.removeEventListener(MouseEvent.MOUSE_UP, onRelease);
			stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
		}
		dragged = false;
	}
	/**
	 * 
	 * @param rect Rectangle
	 * 
	 */	
	public function setDragBounds (rect:Rectangle):void
	{
		bounds = rect;
		boundsInitialized = true;
		place(x,y);
	}
	/**
	 * Возвращаем объект на точку, с которой взяли в последний раз
	 * @param p
	 */		
	public function restorePosition (p:Point = null):void
	{
		if (p) firstPosition = p;
		place(firstPosition.x, firstPosition.y);	
	}
	/**
	 * 
	 * @param b
	 * 
	 */
	public function set moveable (b:Boolean):void 
	{
		if (dragged && !b)
		{
			dispatchEvent(new DragEvent(DragEvent.STOP_DRAG));
			endDrag();
		}
		_moveable = b;
	}
	/**
	 * 
	 * @return 
	 * 
	 */
	public function get moveable ():Boolean {return _moveable;}
	/**
	 * 
	 * @param b
	 * 
	 */
	public function set enabled(b:Boolean):void 
	{
		_enabled = b;
		if (stage==null) return;
		
		if (b)
		{
			addEventListener(MouseEvent.MOUSE_DOWN, onPress, false,0,true);
			stage.addEventListener(Event.RESIZE, onStageResize, false, 0, true);
			buttonMode = useHandCursor = true;
		}
		else
		{
			removeEventListener(MouseEvent.MOUSE_DOWN, onPress);
			stage.removeEventListener(Event.RESIZE, onStageResize);
			buttonMode = useHandCursor = false;
		}
	}
	/**
	 * 
	 * @return 
	 * 
	 */
	public function get enabled():Boolean {return _enabled}
	/**
	 * 
	 * @param value
	 * 
	 */
	override public function set x (value:Number):void
	{
		super.x = value;
		firstPosition.x = value;
	}
	/**
	 * 
	 * @param value
	 * 
	 */
	override public function set y (value:Number):void
	{
		super.y = value;
		firstPosition.y = value;
	}
	/**
	 * 
	 * 
	 */
	protected function bringToFont():void
	{
		if (parent)
		{
			parent.setChildIndex(this, parent.numChildren-1);
		}
	}
	/**
	 * Точка изменения положения объекта
	 * 
	 * @param x
	 * @param y
	 * 
	 */
	protected function place (x:int, y:int):void
	{
		if (x > bounds.right - width) x = bounds.right - width;
		else if (x < bounds.left) x = bounds.left;
		
		if (y > bounds.bottom - height) y = bounds.bottom - height;
		else if (y < bounds.top) y = bounds.top;
			
		super.x = x;
		super.y = y;
	}
	/**
	 * 
	 * @param e
	 * 
	 */
	private function onPress (e:MouseEvent):void
	{
		dispatchEvent(new DragEvent(DragEvent.START_DRAG, new Point(x, y), e.ctrlKey, e.shiftKey));
		beginDrag(new Point(mouseX, mouseY));
	}
	/**
	 * 
	 * @param e
	 * 
	 */
	private function onRelease (e:MouseEvent):void
	{
		dispatchEvent(new DragEvent(DragEvent.STOP_DRAG, new Point(x, y), e.ctrlKey, e.shiftKey));
		endDrag();
	}
	/**
	 * 
	 * @param e
	 * 
	 */
	private function onMouseMove (e:MouseEvent):void
	{
		if (dragged)
		{
			place(parent.mouseX - dragAnchor.x, parent.mouseY - dragAnchor.y);
			
			dispatchEvent(new DragEvent(DragEvent.MOVE, new Point(x, y), e.ctrlKey, e.shiftKey));
			
			e.updateAfterEvent();
		}
	}
	/**
	 * 
	 * @param e
	 * 
	 */
	private function onStageResize (e:Event):void
	{
		if (! boundsInitialized)
		{
			var p:Point = parent.globalToLocal(new Point(0, 0));
			bounds = new Rectangle(p.x, p.y, stage.stageWidth, stage.stageHeight);
		}
	}
	/**
	 * 
	 * @param e
	 * 
	 */	
	private function onAddedToStage (e:Event):void
	{
		enabled = _enabled;

		if (bounds)
		{
			boundsInitialized = true;
		}
			
		firstPosition = new Point(x, y);	
	}
	/**
	 * 
	 * @param e
	 * 
	 */
	private function onRemovedFromStage (e:Event):void
	{
		stage.removeEventListener(MouseEvent.MOUSE_UP, onRelease);
		stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
		stage.removeEventListener(Event.RESIZE, onStageResize);
	}

}
}

